﻿using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using LayoutViewer.Views;
using Newtonsoft.Json;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LayoutViewer.Models.Helpers
{
    internal class MessageType
    {
        public static byte Stop { get; set; } = 0;
        public static byte Ping { get; set; } = 1;
        public static byte ScreenShot { get; set; } = 2;
        public static byte LayoutViewer { get; set; } = 3;
    }
    internal class TClient
    {
        public TcpClient Client { get; set; }
        public string DeviceName { get; set; }

        public TClient(TcpClient client, string deviceName)
        {
            this.Client = client;
            this.DeviceName = deviceName;
        }

    }
    public class TcpHelper : IHelper
    {
        public Action<Bitmap, MoveCategory>? Action { get; set; }

        private TcpListener? Server;
        private List<TClient> Clients = new();

        private string localIP = string.Empty;

        public TcpHelper(string localIP)
        {
            this.localIP = localIP;
            Init();
        }
        public void Init()
        {
            this.Server = new TcpListener(IPAddress.Parse(this.localIP), 5678);
            this.Server.Start();
            this.Server.BeginAcceptTcpClient(new AsyncCallback(ConnectCallback), this.Server);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            if (ar.AsyncState != null)
            {
                var listener = (TcpListener)ar.AsyncState;

                if (listener.Server == null || !listener.Server.IsBound)
                {
                    return;
                }

                try
                {
                    var client = listener.EndAcceptTcpClient(ar);
                    var stream = client.GetStream();
                    var buf = new byte[256];
                    for (var i = 0; i < 40; i++)
                    {
                        Task.Delay(100).Wait();
                        if (stream.DataAvailable)
                        {
                            var length = stream.Read(buf, 0, 256);
                            var info = Encoding.UTF8.GetString(buf, 0, length);
                            this.Clients.Add(new TClient(client, info));
                            this.Server?.BeginAcceptTcpClient(new AsyncCallback(ConnectCallback), this.Server);
                            return;
                        }
                    }
                }
                catch (Exception e)
                {
                    MessageBox.ShowAsync(e.ToString());
                }
            }
        }

        public void Dispose()
        {
            try
            {
                this.Server?.Stop();
                foreach (var client in this.Clients)
                {
                    client.Client.Close();
                    client.Client.Dispose();
                }
            }
            catch { }
        }

        public void GetScreenAndLayout(int index)
        {
            Task.Run(() =>
            {
                try
                {
                    var stream = this.Clients[index].Client.GetStream();
                    if (!Ping(stream))
                    {
                        throw new Exception("Tcp已断开连接! 请重新连接");
                    }
                    stream.WriteByte(MessageType.LayoutViewer);

                    var offset = 0;
                    var imgHeader = new byte[4];
                    var layoutHeader = new byte[4];
                    for (var i = 0; i < 100; i++)
                    {
                        Thread.Sleep(50);
                        if (stream.DataAvailable)
                        {
                            while (offset < 4)
                            {
                                var len = stream.Read(imgHeader, offset, 4 - offset);
                                offset += len;
                            }

                            offset = 0;

                            while (offset < 4)
                            {
                                var len = stream.Read(layoutHeader, offset, 4 - offset);
                                offset += len;
                            }
                            break;
                        }
                    }

                    var imgLen = imgHeader.ToInt();
                    var layoutrLen = layoutHeader.ToInt();


                    var imgData = new byte[imgLen];

                    offset = 0;

                    while (offset < imgLen)
                    {
                        if (stream.DataAvailable)
                        {
                            var len = stream.Read(imgData, offset, imgLen - offset);
                            offset += len;
                        }
                    }

                    var sKBitmap = SKBitmap.Decode(imgData);
                    var pxFormat = sKBitmap.ColorType == SKColorType.Rgba8888 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888;
                    var bitmap = new Bitmap(pxFormat, AlphaFormat.Opaque, sKBitmap.GetPixels(), new PixelSize(sKBitmap.Width, sKBitmap.Height), new Vector(96, 96), sKBitmap.RowBytes);
                    sKBitmap.Dispose();

                    var layoutData = new byte[layoutrLen];

                    offset = 0;

                    while (offset < layoutrLen)
                    {
                        if (stream.DataAvailable)
                        {
                            var len = stream.Read(layoutData, offset, layoutrLen - offset);
                            offset += len;
                        }
                    }
                    var str = Encoding.UTF8.GetString(layoutData, 0, layoutrLen);
                    var layout = JsonConvert.DeserializeObject<MoveCategory>(str) ?? new MoveCategory();

                    this.Action?.Invoke(bitmap, layout);
                    this.Server?.BeginAcceptTcpClient(new AsyncCallback(ConnectCallback), this.Server);

                }
                catch (Exception e) { }
            });
        }

        private static bool Ping(NetworkStream stream)
        {
            try
            {
                for (var j = 0; j < 20; j++)
                {
                    stream.WriteByte(MessageType.Ping);
                    for (var i = 0; i < 10; i++)
                    {
                        Thread.Sleep(50);
                        var temp = new byte[9];
                        if (stream.DataAvailable)
                        {
                            var len = stream.Read(temp, 0, 1);
                            if (len == 1)
                            {
                                if (temp[0] == MessageType.Ping)
                                {
                                    return true;
                                }
                                else if (temp[0] == MessageType.Stop)
                                {
                                    return false;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                MessageBox.ShowAsync(e.Message);
            }
            return false;
        }


        public Task<ObservableCollection<string>> List()
        {
            return Task.Run(() =>
            {
                var result = new ObservableCollection<string>();
                foreach (var client in this.Clients)
                {
                    result.Add(client.DeviceName);
                }
                return result;
            });
        }
    }
}
